From dcba77fcc454229079049f41f4ddc23e5c27353f Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Thu, 6 Mar 2014 14:50:35 +0100 Subject: [PATCH] popover: Track toplevel focus changes Make the popover temporarily undo the GTK+ grab, so it remains modal to its window, but does not attempt to steal focus on other non-modal windows that get the focus. This was most confusing with keyboard navigation, as the focus would remain stuck on the popover, and not move to the newly focused window after the popover was dismissed. It didn't have as much effect on pointer operations as only the first click would be consumed in order to hide the popover. --- gtk/gtkpopover.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/gtk/gtkpopover.c b/gtk/gtkpopover.c index 1cc9e9c5d4..2286cff8f4 100644 --- a/gtk/gtkpopover.c +++ b/gtk/gtkpopover.c @@ -102,6 +102,7 @@ struct _GtkPopoverPrivate guint modal : 1; guint button_pressed : 1; guint apply_shape : 1; + guint grab_notify_blocked : 1; }; static GQuark quark_widget_popovers = 0; @@ -254,6 +255,52 @@ gtk_popover_realize (GtkWidget *widget) gtk_widget_set_realized (widget, TRUE); } +static gboolean +window_focus_in (GtkWidget *widget, + GdkEvent *event, + GtkPopover *popover) +{ + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); + + /* Regain the grab when the window is focused */ + if (priv->modal && + gtk_widget_is_drawable (GTK_WIDGET (popover))) + { + GtkWidget *focus; + + gtk_grab_add (GTK_WIDGET (popover)); + + focus = gtk_window_get_focus (GTK_WINDOW (widget)); + + if (!gtk_widget_is_ancestor (focus, GTK_WIDGET (popover))) + gtk_widget_grab_focus (GTK_WIDGET (popover)); + + if (priv->grab_notify_blocked) + g_signal_handler_unblock (priv->widget, priv->grab_notify_id); + + priv->grab_notify_blocked = FALSE; + } + return FALSE; +} + +static gboolean +window_focus_out (GtkWidget *widget, + GdkEvent *event, + GtkPopover *popover) +{ + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); + + /* Temporarily remove the grab when unfocused */ + if (priv->modal && + gtk_widget_is_drawable (GTK_WIDGET (popover))) + { + g_signal_handler_block (priv->widget, priv->grab_notify_id); + gtk_grab_remove (GTK_WIDGET (popover)); + priv->grab_notify_blocked = TRUE; + } + return FALSE; +} + static void gtk_popover_apply_modality (GtkPopover *popover, gboolean modal) @@ -270,9 +317,15 @@ gtk_popover_apply_modality (GtkPopover *popover, g_object_ref (prev_focus); gtk_grab_add (GTK_WIDGET (popover)); gtk_widget_grab_focus (GTK_WIDGET (popover)); + + g_signal_connect (priv->window, "focus-in-event", + G_CALLBACK (window_focus_in), popover); + g_signal_connect (priv->window, "focus-out-event", + G_CALLBACK (window_focus_out), popover); } else { + g_signal_handlers_disconnect_by_data (priv->window, popover); gtk_grab_remove (GTK_WIDGET (popover)); if (priv->prev_focus_widget) -- 2.30.2